#ifndef PROCEDURAL_OPERATOR_H_INCLUDED
#define PROCEDURAL_OPERATOR_H_INCLUDED

#include "ProceduralPlatform.h"
#include "ProceduralUtils.h"
#include "ProceduralTriangleBuffer.h"
#include "ProceduralShape.h"
#include "ProceduralPath.h"
#include "OgreAny.h"

namespace Procedural 
{
	using namespace std;

#define RELATIVE_ADRESS(v) (char*)((char*)&v-(char*)(Operator*)this)
#define RELATIVE_ADRESSFROM(v, p) (char*)((char*)&v-(char*)p)

enum _ProceduralExport BufferType
{
	// OgreProcedural types
	BT_TRIANGLEBUFFER, 
	BT_SHAPE, 
	BT_PATH, 
	BT_TRACK, 
	// Ogre and standard types
	BT_SHORT,
	BT_USHORT,
	BT_INT,
	BT_UINT,
	BT_LONG,
	BT_ULONG,
	BT_STRING,
	BT_BOOL,
	BT_REAL, 
	BT_VEC2,
	BT_VEC3,
	BT_VEC4,
	BT_QUAT,
	BT_UNKNOWN
};

class Operator;

class OperatorSetterBase
{
public:	
	virtual BufferType getParamType() { return BT_UNKNOWN;}
	virtual void set(const Ogre::Any& value, Operator* o) {}
};

	template <typename T> static BufferType getBufferType() { return BT_UNKNOWN;}
	template <> static BufferType getBufferType<Ogre::Real>() { return BT_REAL;}
	template <> static BufferType getBufferType<Ogre::Vector2>() { return BT_VEC2;}
	template <> static BufferType getBufferType<Ogre::Vector3>() { return BT_VEC3;}
	template <> static BufferType getBufferType<Ogre::Quaternion>() { return BT_QUAT;}
	template <> static BufferType getBufferType<unsigned short>() { return BT_USHORT;}
	template <> static BufferType getBufferType<short>() { return BT_SHORT;}
	template <> static BufferType getBufferType<unsigned int>() { return BT_UINT;}
	template <> static BufferType getBufferType<int>() { return BT_INT;}
	template <> static BufferType getBufferType<unsigned long>() { return BT_ULONG;}
	template <> static BufferType getBufferType<long>() { return BT_LONG;}
	template <> static BufferType getBufferType<Shape*>() { return BT_SHAPE;}
	template <> static BufferType getBufferType<Path*>() { return BT_PATH;}
	template <> static BufferType getBufferType<Track*>() { return BT_TRACK;}
	template <> static BufferType getBufferType<TriangleBuffer*>() { return BT_TRIANGLEBUFFER;}

struct _ProceduralExport InputParam
{
	InputParam() : mPointer(0) {}
	InputParam(string name, BufferType type, char* pointer) :  mName(name), mType(type), mPointer((int)pointer), mUseCustomSetter(false) {}
	InputParam(string name, BufferType type, OperatorSetterBase* funcpointer) :  mName(name), mType(type), mUseCustomSetter(true), mSetter(funcpointer) {}
	string mName;
	BufferType mType;
	int mPointer;
	bool mUseCustomSetter;
	OperatorSetterBase* mSetter;


};

template<typename T, typename I, typename R=T>
class OperatorSetterChainable : public OperatorSetterBase
{	
public:
	typedef R&(T::*SetterType)(I);
private:
	SetterType mFunc;
public:
	OperatorSetterChainable(SetterType func)
	{
		mFunc = func;
	}

	BufferType getParamType()
	{
		return InputParam::getBufferType<I>();
	}

	void set(const Ogre::Any& value, Operator* o)
	{
		R* t = static_cast<R*>(o);
		(t->*mFunc)(Ogre::any_cast<I>(value));
	}
};

template<typename T, typename I, typename R=T>
class OperatorSetterChainableConstRef : public OperatorSetterBase
{	
public:
	typedef R&(T::*SetterType)(const I&);
private:
	SetterType mFunc;
public:
	OperatorSetterChainableConstRef(SetterType func)
	{
		mFunc = func;
	}

	BufferType getParamType()
	{
		return getBufferType<I>();
	}

	void set(const Ogre::Any& value, Operator* o)
	{
		R* t = static_cast<R*>(o);
		(t->*mFunc)(Ogre::any_cast<I>(value));
	}
};

struct _ProceduralExport OperatorDefinition
{
	string mOperatorName;
	std::map<string, InputParam> mInputParamList;
	BufferType mOutputType;
	Operator* defaultInstance;

	template<typename T>
	T getInputParam(string paramName, Operator* thispointer)
	{
		int offset =  (mInputParamList[paramName].mPointer);
		return *((T*)(offset+(int)thispointer));
	}

	template<typename T>
	void setInputParam(string paramName, Operator* thispointer, T newVal)
	{
		const InputParam& ip = mInputParamList[paramName];
		if (ip.mUseCustomSetter)
		{
			ip.mSetter->set(Ogre::Any(newVal), thispointer);
		}
		else
		*((T*)(ip.mPointer+(int)thispointer)) = newVal;
	}

	template <typename T>
	inline BufferType getBufferTypeFromInstance(T t) { return getBufferType<T>();}

	template<typename T, class U>
	inline void addInputParam(string name, T& pointer, U* op)
	{
		char* p = (char*)((char*)&pointer-(char*)(Operator*)op);
		InputParam ip(name, getBufferTypeFromInstance(pointer), p);
		mInputParamList[name] = ip;
	}

	inline void addInputParamFunc(string name, OperatorSetterBase* funcpointer)
	{
		InputParam ip(name, funcpointer->getParamType(), funcpointer);
		mInputParamList[name] = ip;
	}
		
	void logDescription()
	{
		Utils::log("\tOperator " + mOperatorName);
		for (std::map<string, InputParam>::iterator it = mInputParamList.begin(); it!= mInputParamList.end();it++)
		{
			Utils::log("\t\tParameter : " + it->second.mName);
		}	
	}
};

class _ProceduralExport Operator
{
	public:
	std::map<string, Operator*> mDependencies;

	void* outputCache;	
	
	virtual void getDefinition(OperatorDefinition& operatorDef)=0;

	virtual void buildOutput() = 0;

	virtual Operator* clone() = 0;
};

template <typename T>
class OperatorImpl : public Operator
{
public:
	void buildOutput() {}

	void getDefinition(OperatorDefinition& operatorDef) {}

	Operator* clone()
	{
		Operator* o = new T(static_cast<const T&>(*this));
		return o;
	}
};


class _ProceduralExport OperatorManager
{
	std::map<string, OperatorDefinition> mOperators;
public:

	template <class T>
	void registerOperator()
	{
		T* t = new T();
		Operator* p = static_cast<Operator*>(t);
		OperatorDefinition op;
		p->getDefinition(op);
		op.defaultInstance = t;
		mOperators[op.mOperatorName] = op;

		Utils::log("Registered operator " + op.mOperatorName);
		op.logDescription();
	}

	std::vector<string> tokenize(const string& s)
	{
		// remove comments and left spaces
		size_t begin = s.find_first_not_of("\t ");
		size_t end = s.find("//");
		string s2;
		if (begin != string::npos && end!=string::npos)
			s2 = s.substr(begin, end-1);
		else if (begin != string::npos)
			s2 = s.substr(begin);
		else
			s2 = s;
		// tokenize
		size_t tokenPos=0;
		size_t lastTokenPos=0;
		std::vector<string> out;
		while ((tokenPos = s2.find(" ", tokenPos+1))!=string::npos && out.size()<1)
		{
			out.push_back(s2.substr(lastTokenPos, tokenPos));
			lastTokenPos=tokenPos+1;
		}
		if (lastTokenPos<s2.size())
			out.push_back(s2.substr(lastTokenPos));
		return out;
	}

	void deserialize(string fileName);
};

}

#endif